package com.smart.manager.system.repository;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.smart.common.entities.constant.ExceptionTips;
import com.smart.common.entities.core.Page;
import com.smart.common.entities.domain.TaskResult;
import com.smart.common.exception.SmartException;
import com.smart.manager.system.entities.body.RSystemScheduleTask;
import com.smart.manager.system.entities.enums.TaskRunMode;
import com.smart.manager.system.entities.enums.TaskRunResult;
import com.smart.manager.system.entities.enums.TaskState;
import com.smart.manager.system.entities.models.SystemScheduleRecord;
import com.smart.manager.system.entities.models.SystemScheduleTask;
import com.smart.manager.system.entities.models.query.QSystemScheduleRecord;
import com.smart.manager.system.entities.models.query.QSystemScheduleTask;
import com.smart.manager.system.entities.views.TaskView;
import com.smart.manager.system.handler.TaskRegHandler;
import com.smart.manager.system.handler.TaskRunHandler;
import io.ebean.DB;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author Administrator
 */
@Service
@Log4j2
@RequiredArgsConstructor
public class ScheduleTaskRepository {


    private final TaskRegHandler taskRegHandler;


    /**
     * 分页查询列表
     *
     * @param search 搜索
     * @return {@code Page<Task>}
     */
    public Page<SystemScheduleTask> pageList(RSystemScheduleTask.SearchBody search) {
        QSystemScheduleTask query = new QSystemScheduleTask();
        Opt.ofBlankAble(search.getTitle()).ifPresent(x -> query.title.contains(x));
        Opt.ofBlankAble(search.getRemark()).ifPresent(x -> query.remark.contains(x));
        Opt.ofBlankAble(search.getState()).ifPresent(x -> query.state.eq(x));
        query.setFirstRow(search.firstRow()).setMaxRows(search.maxRows()).order().id.desc();
        return new Page<SystemScheduleTask>().load(query.findPagedList());
    }

    /**
     * 查询运行任务列表
     *
     * @return {@code List<Task>}
     */

    public List<SystemScheduleTask> getRunTaskList() {
        return new QSystemScheduleTask().state.eq(TaskState.NORMAL).findList();
    }


    /**
     * 注册任务
     *
     * @param taskId 任务id
     */

    public void registerTask(Long taskId) {
        SystemScheduleTask task = new QSystemScheduleTask().id.eq(taskId).findOne();
        TaskRunHandler taskRunHandler = new TaskRunHandler(task);
        taskRegHandler.addTask(taskRunHandler, task.getCron());
    }


    /**
     * 根据任务ID终止任务
     */

    public void stopTask(Long taskId) {
        SystemScheduleTask task = new QSystemScheduleTask().id.eq(taskId).findOne();
        //获取任务对象实例
        TaskRunHandler taskRunHandler = new TaskRunHandler(task);
        taskRegHandler.removeCronTask(taskRunHandler);
    }


    /**
     * 手动执行
     *
     * @param taskId 任务id
     */
    @Async
    public void runNow(Long taskId) {
        SystemScheduleTask task = new QSystemScheduleTask().id.eq(taskId).setMaxRows(1).findOne();
        Assert.notNull(task, "任务信息不存在");
        TaskView taskView = new TaskView(taskId);
        SystemScheduleRecord taskLog = new SystemScheduleRecord().setRunMode(TaskRunMode.MANUAL).setTask(taskView).setRunTime(LocalDateTime.now());
        TaskResult taskResult;
        Object invoke;
        try {
            Object target = SpringUtil.getBean(task.getApp().getBeanName());
            if (StrUtil.isNotBlank(task.getParams())) {
                //执行有参任务执行方法
                Method method = target.getClass().getDeclaredMethod(task.getApp().getMethodName(), String.class);
                ReflectionUtils.makeAccessible(method);
                invoke = method.invoke(target, task.getParams());
            } else {
                //执行无参方法
                Method method = target.getClass().getDeclaredMethod(task.getApp().getMethodName());
                ReflectionUtils.makeAccessible(method);
                invoke = method.invoke(target);
            }

            taskResult = (TaskResult) invoke;
            TaskRunResult runResult = taskResult.getStatus() ? TaskRunResult.SUCCESS : TaskRunResult.FAIL;
            taskLog.setRunResult(runResult).setMessage(taskResult.getMessage());

        } catch (Exception e) {
            e.printStackTrace();
            String message = String.format("定时任务执行异常:bean：%s ,方法：%s  , 参数：%s  ,消息：%s", task.getApp().getBeanName(), task.getApp().getMethodName(), task.getParams(), e.getMessage());
            taskLog.setRunResult(TaskRunResult.FAIL).setMessage(message);
            throw new SmartException(message);
        } finally {
            updateLastRunTime(taskId);
            taskLog.save();
        }


    }


    /**
     * 更新最后一次运行时
     *
     * @param taskId 任务id
     */

    public void updateLastRunTime(Long taskId) {
        SystemScheduleTask task = new QSystemScheduleTask().id.eq(taskId).setMaxRows(1).findOne();
        Opt.ofNullable(task).ifPresent(x -> x.setLastRunTime(LocalDateTime.now()).update());
    }

    /**
     * 新增定时任务
     *
     * @param data 数据
     */

    public void save(RSystemScheduleTask.EditBody data) {
        SystemScheduleTask insEntity = BeanUtil.copyProperties(data, SystemScheduleTask.class);
        long taskId = IdUtil.getSnowflakeNextId();
        insEntity.setId(taskId);
        insEntity.save();
        if (TaskState.NORMAL.equals(insEntity.getState())) {
            this.registerTask(taskId);
        }

    }

    /**
     * 更新任务
     *
     * @param id   id
     * @param data 数据
     */

    public void update(Long id, RSystemScheduleTask.EditBody data) {
        SystemScheduleTask record = new QSystemScheduleTask().id.eq(id).findOne();
        Assert.notNull(record, ExceptionTips.NOT_FOUND);
        this.stopTask(id);
        BeanUtil.copyProperties(data, record);
        record.update();
        if (TaskState.NORMAL.equals(record.getState())) {
            this.registerTask(id);
        }
    }

    /**
     * 删除根据id列表
     *
     * @param idList id列表
     * @return int
     */

    @Transactional(rollbackFor = Exception.class)
    public int removeByIdList(List<Long> idList) {
        List<SystemScheduleTask> list = new QSystemScheduleTask().id.in(idList).findList();
        Assert.notEmpty(list, ExceptionTips.NOT_FOUND);
        list.forEach(x -> {
            if (TaskState.NORMAL.equals(x.getState())) {
                this.stopTask(x.getId());
            }
        });

        new QSystemScheduleRecord().task.id.in(idList).delete();
        return DB.deleteAll(list);
    }


}
